#ifndef _PCIe_TYPES_H
#define _PCIe_TYPES_H

#include "PCIe_define.h"

typedef unsigned	PCIe_byte_length_t;

typedef enum
{
	x1	= 1,
	x2	= 2,
	x4	= 4,
	x8	= 8,
	x12	= 12,
	x16	= 16,
	x32	= 32
} PCIe_lane_t;

typedef enum
{
	IDLE	= 0,
	STP		= 1,
	SDP		= 2,
	END		= 3
} PCIe_symbol_t;

typedef enum
{
	MEMORY					= 0,
	MEMORY_READ_LOCK		= 1,
	I_O						= 2,
	CONFIGURATION_TYPE0		= 4,
	CONFIGURATION_TYPE1		= 5,
	COMPLETION				= 10,
	COMPLETION_LOCK			= 11,
	MESSAGE_RC				= 16,
	MESSAGE_ADDRESS			= 17,
	MESSAGE_ID				= 18,
	MESSAGE_BROADCAST		= 19,
	MESSAGE_LOCAL			= 20,
	MESSAGE_GATHERED_TO_RC	= 21
} PCIe_TLP_type_t;

typedef enum
{
	ACK					= 0,
	NAK					= 16,
	PM_ENTER_L1			= 32,
	PM_ENTER_L23		= 33,
	PM_ACT_STATE_REQ_L1	= 35,
	PM_REQ_ACK			= 36,
	VENDOR_SPECIFIC		= 48,

	INIT_FC1_P_VC0		= 64,
	INIT_FC1_P_VC1		= 65,
	INIT_FC1_P_VC2		= 66,
	INIT_FC1_P_VC3		= 67,
	INIT_FC1_P_VC4		= 68,
	INIT_FC1_P_VC5		= 69,
	INIT_FC1_P_VC6		= 70,
	INIT_FC1_P_VC7		= 71,
	INIT_FC1_NP_VC0		= 80,
	INIT_FC1_NP_VC1		= 81,
	INIT_FC1_NP_VC2		= 82,
	INIT_FC1_NP_VC3		= 83,
	INIT_FC1_NP_VC4		= 84,
	INIT_FC1_NP_VC5		= 85,
	INIT_FC1_NP_VC6		= 86,
	INIT_FC1_NP_VC7		= 87,
	INIT_FC1_CPL_VC0	= 96,
	INIT_FC1_CPL_VC1	= 97,
	INIT_FC1_CPL_VC2	= 98,
	INIT_FC1_CPL_VC3	= 99,
	INIT_FC1_CPL_VC4	= 100,
	INIT_FC1_CPL_VC5	= 101,
	INIT_FC1_CPL_VC6	= 102,
	INIT_FC1_CPL_VC7	= 103,

	INIT_FC2_P_VC0		= 192,
	INIT_FC2_P_VC1		= 193,
	INIT_FC2_P_VC2		= 194,
	INIT_FC2_P_VC3		= 195,
	INIT_FC2_P_VC4		= 196,
	INIT_FC2_P_VC5		= 197,
	INIT_FC2_P_VC6		= 198,
	INIT_FC2_P_VC7		= 199,
	INIT_FC2_NP_VC0		= 208,
	INIT_FC2_NP_VC1		= 209,
	INIT_FC2_NP_VC2		= 210,
	INIT_FC2_NP_VC3		= 211,
	INIT_FC2_NP_VC4		= 212,
	INIT_FC2_NP_VC5		= 213,
	INIT_FC2_NP_VC6		= 214,
	INIT_FC2_NP_VC7		= 215,
	INIT_FC2_CPL_VC0	= 224,
	INIT_FC2_CPL_VC1	= 225,
	INIT_FC2_CPL_VC2	= 226,
	INIT_FC2_CPL_VC3	= 227,
	INIT_FC2_CPL_VC4	= 228,
	INIT_FC2_CPL_VC5	= 229,
	INIT_FC2_CPL_VC6	= 230,
	INIT_FC2_CPL_VC7	= 231,
	
	UPDATE_FC2_P_VC0	= 128,
	UPDATE_FC2_P_VC1	= 129,
	UPDATE_FC2_P_VC2	= 130,
	UPDATE_FC2_P_VC3	= 131,
	UPDATE_FC2_P_VC4	= 132,
	UPDATE_FC2_P_VC5	= 133,
	UPDATE_FC2_P_VC6	= 134,
	UPDATE_FC2_P_VC7	= 135,
	UPDATE_FC2_NP_VC0	= 144,
	UPDATE_FC2_NP_VC1	= 145,
	UPDATE_FC2_NP_VC2	= 146,
	UPDATE_FC2_NP_VC3	= 147,
	UPDATE_FC2_NP_VC4	= 148,
	UPDATE_FC2_NP_VC5	= 149,
	UPDATE_FC2_NP_VC6	= 150,
	UPDATE_FC2_NP_VC7	= 151,
	UPDATE_FC2_CPL_VC0	= 160,
	UPDATE_FC2_CPL_VC1	= 161,
	UPDATE_FC2_CPL_VC2	= 162, 
	UPDATE_FC2_CPL_VC3	= 163,
	UPDATE_FC2_CPL_VC4	= 164,
	UPDATE_FC2_CPL_VC5	= 165,
	UPDATE_FC2_CPL_VC6	= 166,
	UPDATE_FC2_CPL_VC7	= 167
} PCIe_DLLP_type_t;

typedef enum
{
	HOST_DRAM_CONTROLLER_ID		= 0x0,
	EXTERNAL_GRAPHIC_ID			= 0x8,
	INTERNAL_GRAPHIC_1_ID		= 0x10,
	INTERNAL_GRAPHIC_2_ID		= 0x11,
	MANAGEABILITY_ENGINE_1_ID	= 0x18,
	MANAGEABILITY_ENGINE_2_ID	= 0x19,
	MANAGEABILITY_ENGINE_3_ID	= 0x1A,
	PCI_BRIDGE_ID				= 0xF0,
	PCI_EXPRESS_ROOT_PORT_1_ID	= 0xE0,
	PCI_EXPRESS_ROOT_PORT_2_ID	= 0xE1,
	PCI_EXPRESS_ROOT_PORT_3_ID	= 0xE2,
	PCI_EXPRESS_ROOT_PORT_4_ID	= 0xE3,
	PCI_EXPRESS_ROOT_PORT_5_ID	= 0xE4,
	PCI_EXPRESS_ROOT_PORT_6_ID	= 0xE5,
	ETHERNET_CONTROLLER_ID		= 0xC8,
	LPC_BRIDGE_ID				= 0xF8,
	SATA_CONTROLLER_1_ID		= 0xFA,
	SATA_CONTROLLER_2_ID		= 0xFD,
	USB_UHCI_CONTROLLER_1_ID	= 0xD0,
	USB_UHCI_CONTROLLER_2_ID	= 0xD1,
	USB_UHCI_CONTROLLER_3_ID	= 0xD2,
	USB_UHCI_CONTROLLER_4_ID	= 0xE8,
	USB_UHCI_CONTROLLER_5_ID	= 0xE9,
	USB_UHCI_CONTROLLER_6_ID	= 0xEA,
	USB_EHCI_CONTROLLER_1_ID	= 0xD7,
	USB_EHCI_CONTROLLER_2_ID	= 0xEF,
	SMBUS_CONTROLLER_ID			= 0xFB,
	HD_AUDIO_CONTROLLER_ID		= 0xD8,
	THERMAL_SENSOR_ID			= 0xFE,
	SWITCH_ID					= 0x80,
	NORTH_DMA_ID				= 0x81,
	SOUTH_DMA_ID				= 0x82,
} PCIe_id_t;

typedef enum
{
	MEM		= 0,
	IO		= 1,
	CONF	= 2
} PCIe_address_type_t;

typedef enum
{
	PL_IDLE		= 0,
	TLP_SEQ_NUM	= 1,
	TLP_HEADER	= 2,
	TLP_DATA	= 3,
	TLP_ECRC	= 4,
	TLP_LCRC	= 5,
	DLLP_TYPE	= 6,
	DLLP_DATA	= 7,
	DLLP_CRC	= 8
} PCIe_state_t;

struct PCIe_TLP
{
	bool				with_data;
	bool				DW4_header;
	byte*				header;
	
	PCIe_byte_length_t	length;
	unsigned*			data;

	bool				TLP_digest;
	unsigned			ECRC;

	PCIe_TLP()
		: with_data		(false)
		, DW4_header	(false)
		, header		(NULL)
		, length		(0)
		, data			(NULL)
		, TLP_digest	(true)
		, ECRC			(0)
	{}
};

struct PCIe_DLL_TLP
{
	unsigned short	sequence_number;
	PCIe_TLP		TLP;
	unsigned		LCRC;

	PCIe_DLL_TLP()
		: sequence_number	(0)
		, LCRC				(0)
	{}
};

struct PCIe_DLLP
{
	PCIe_DLLP_type_t	type;
	byte				data[3];
	unsigned short		CRC;

	PCIe_DLLP()
		: type	(ACK)
		, CRC	(0)
	{}
};

struct PCIe_address
{
	unsigned	high_address;
	unsigned	low_address;

	PCIe_address()
		: high_address	(0)
		, low_address	(0)
	{}

	PCIe_address(unsigned	high_addr,
				 unsigned	low_addr)
		: high_address	(high_addr)
		, low_address	(low_addr)
	{}
};

struct PCIe_address_space
{
public:
	PCIe_address_type_t	type;
	PCIe_address		start_addr;
	PCIe_address		end_addr;

	PCIe_address_space()
		: type			(MEM)
		, start_addr	(0,0)
		, end_addr		(0,0)
	{}

	PCIe_address_space(PCIe_address_type_t	type_,
					   unsigned				start_addr_high_,
					   unsigned				start_addr_low_,
					   unsigned				end_addr_high_,
					   unsigned				end_addr_low_)
		: type			(type_)
	{
		start_addr.high_address = start_addr_high_;
		start_addr.low_address = start_addr_low_;
		end_addr.high_address = end_addr_high_;
		end_addr.low_address = end_addr_low_;
	}
};

class PCIe_address_map
{
public:
	PCIe_address_map(unsigned alloc_ = PCIe_NUM_SLAVES)
		: _alloc	(0)
		, _size		(0)
		, _space	(0)
	{
		_resize(alloc_);
	}

	~PCIe_address_map()
	{
		for(int i=0; i<_size; i++)
			delete _space[i];

		if(_space)
			delete[] _space;
	}

	void
	push_back(const PCIe_address_space& space_)
	{
		if(_size == _alloc)
			_resize(_alloc+PCIe_NUM_SLAVES);
		_space[_size] = new PCIe_address_space(space_);
		_size++;
	}

	const unsigned
	size() const
	{
		return _size;
	}

	const PCIe_address_space&
	operator[](int i) const
	{
		sc_assert((i>=0) && (i<_size));
		return *_space[i];
	}

private:
	unsigned _alloc;
	unsigned _size;
	PCIe_address_space** _space;

	void
	_resize(int new_size)
	{
		PCIe_address_space** new_space;

		if( new_size <= _alloc ) {
			return;
		}
		_alloc = new_size;
		new_space = new PCIe_address_space*[new_size];
		for(int i=0; i<_size; i++)
			new_space[i] = _space[i];
		if(_space)
			delete[] _space;
		_space = new_space;
	}
};

#endif